#include <ctype.h>
#include <assert.h>
#include <string.h>
#include <wctype.h>
#include <stdint.h>
#include <unistd.h>

#ifdef __cplusplus
extern "C" {
#endif

static uint32_t _c2i(uint8_t ch)
{
    if(isdigit(ch))
        return ch - 48;

    if(ch < 'A' || (ch > 'F' && ch < 'a') || ch > 'z')
        return 0xffffffff;

    if(isalpha(ch))
        return isupper(ch) ? ch - 55 : ch - 87;

    return 0xffffffff;
}

uint8_t convert_char2hex(int ch)
{
    assert(0 != isxdigit(ch));
    if(isdigit(ch))
        return (uint8_t)(ch - '0');
    else
        return (towupper(ch) - 'A' + 10);
}

int convert_hex2char(uint8_t hex)
{
    assert(hex < 16);
    if(hex < 10)
        return ('0' + hex);
    else
        return ('A' + hex - 10);
}

uint32_t convert_hex2dec(const char *hex)
{
    uint32_t len = 0, num = 0, temp = 0, bits = 0, i = 0;

    len = (uint32_t)strlen(hex);
    for(i = 0, temp = 0; i < len; i++, temp = 0)
    {
        temp = _c2i(* (hex + i));
        bits = (len - i - 1) * 4;
        temp = temp << bits;
        num = num | temp;
    }

    return num;
}

int convert_str2hex(uint8_t hex[], size_t len, const char *str)
{
    int i, length = len > strlen(str)/2 ? strlen(str)/2 : len;
    assert(NULL != str);
    assert(NULL != hex);
    for(i = 0 ; i < length; i++)
        hex[i] = convert_char2hex(str[i*2])*16 + convert_char2hex(str[i*2 + 1]);
    return length;
}

int convert_hex2str(char *str, size_t size, const uint8_t hex[], size_t len)
{
    size_t i = 0;
    size_t length = len > size/2 ? size/2 : len;
    assert(NULL != str);
    assert(NULL != hex);
    for(; i < length; i++)
    {
        str[i*2]     = convert_hex2char(hex[i]/16);
        str[i*2 + 1] = convert_hex2char(hex[i]%16);
    }
    if(length*2 == size)
        str[length*2 - 1] = '\0';
    else
        str[length*2] = '\0';
    return length;
}

unsigned long int convert_str2ul(const char *cp,char **endp,unsigned int base)
{
    unsigned long int result = 0,value;
    assert(NULL != cp);
    /* The cp string start with 0 */
    if (*cp == '0')
    {
        cp++;
        /* Begin with '0x' is in base '10' */
        if ((*cp == 'x') && isxdigit(cp[1]))
        {
            base = 16;
            cp++;
        }

        /* Not begin with '0x', and '0 == base' is in base '8' */
        if (!base)
            base = 8;
    }
    /* default base is '10', if '0 == base' */
    if (!base)
        base = 10;

    /*  if '*cp' is character(0~f/F):
     *    if '*cp' is digital, then minus '0';
     *    if '*cp' is upper case, then minus 'A' and plus 10;
     *    if '*cp' is lower case, first convert to upper case, then minus 'A' and plus 10;
     */
    while (isxdigit (*cp) && (value = isdigit (*cp) ? *cp - '0' : (islower (*cp)
        ? toupper (*cp) : *cp) - 'A' + 10) < base)
    {
        result = result*base + value;
        cp++;
    }

    /* if endp exists, the end character is saved to detemine whether the conversion is complete */
    if (endp)
        *endp = (char *)cp;
    
    return result;
}

long int convert_str2l(const char *cp,char **endp,unsigned int base)
{
    if(*cp=='-')
        return -convert_str2ul(cp+1,endp,base);
    return convert_str2ul(cp,endp,base);
}

unsigned long long int convert_str2ull (const char *cp, char **endp, unsigned int base)
{
    unsigned long long int result = 0, value;
    assert(NULL != cp);
    if (*cp == '0')
    {
        cp++;
        if ((*cp == 'x') && isxdigit (cp[1]))
        {
            base = 16;
            cp++;
        }
        if (!base)
            base = 8;
    }
    if (!base)
        base = 10;

    while (isxdigit (*cp) && (value = isdigit (*cp) ? *cp - '0' : (islower (*cp)
        ? toupper (*cp) : *cp) - 'A' + 10) < base)
    {
        result = result * base + value;
        cp++;
    }
    if (endp)
        *endp = (char *) cp;
    return result;
}

long long int convert_str2ll(const char *cp,char **endp,unsigned int base)
{
    if(*cp=='-')
        return -convert_str2ull(cp+1,endp,base);
    return convert_str2ull(cp,endp,base);
}

#if 0
void tm_to_bcd(uint8_t BCD[], struct spos_tm *tm)
{   
    assert(NULL != tm);
    assert(NULL != BCD);
    tm->tm_mon += 1;
    tm->tm_year += 1900;
    BCD[0] = (uint8_t)(tm->tm_year/1000*16 + tm->tm_year%1000/100);
    BCD[1] = (uint8_t)(tm->tm_year%100/10*16 + tm->tm_year%10);
    BCD[2] = (uint8_t)(tm->tm_mon/10*16 + tm->tm_mon%10);
    BCD[3] = (uint8_t)(tm->tm_mday/10*16 + tm->tm_mday%10);
    BCD[4] = (uint8_t)(tm->tm_hour/10*16 + tm->tm_hour%10);
    BCD[5] = (uint8_t)(tm->tm_min/10*16 + tm->tm_min%10);
    BCD[6] = (uint8_t)(tm->tm_sec/10*16 + tm->tm_sec%10);
}

void bcd_to_tm(struct spos_tm *tm, uint8_t BCD[])
{
    assert(NULL != tm);
    assert(NULL != BCD);
    tm->tm_year = (BCD[0]/16)*1000 + (BCD[0]%16)*100 + (BCD[1]/16)*10 + BCD[1]%16;
    tm->tm_mon  = (BCD[2]/16)*10 + BCD[2]%16;
    tm->tm_mday = (BCD[3]/16)*10 + BCD[3]%16;
    tm->tm_hour = (BCD[4]/16)*10 + BCD[4]%16;
    tm->tm_min  = (BCD[5]/16)*10 + BCD[5]%16;
    tm->tm_sec  = (BCD[6]/16)*10 + BCD[6]%16;
    tm->tm_mon -= 1;
    tm->tm_year -= 1900;
}
#endif

#ifdef __cplusplus
}
#endif
